#include "TerrainWater.h"

//Include Render states as we need to disable back fce culling
#include "RenderStates.h"

//Renderer contains a useful function to render textures as a full
//screen quad allowing us to debug it. 
#include "D3D11Renderer.h"

//Include skydome
#include "SkyDome.h"

//Include application to get initial size of the reflecr/refact maps
#include "Application.h"
//Graphics itnerface for the device
#include "GraphicsInterface.h"
//For MSAA state
#include "GraphicsManager.h"

//Effect used when rendering and the shader manager to access it. 
#include "ShaderManager.h"
#include "TerrainWaterFX.h"
#include "TileForwardTextureMapFX.h"
#include "TerrainFX.h"

//Geo gen to create grid
#include "GeometryGenerator.h"

TerrainWater::TerrainWater()
{
	effect = 0;

	//Init D3D resources
	vb = 0;
	ib = 0;
	normalMap = 0;

	underwaterVB = 0;
	underwaterIB = 0;
	underWaterTex = 0;

	reflectionMapSRV = 0;
	refactionMapSRV = 0;
	reflectionMapRTV = 0;
	refactionMapRTV = 0;
	reflectionMapDSV = 0;
	refactionMapDSV = 0;
}

TerrainWater::~TerrainWater()
{
	//Delete D3D resources
	ReleaseCOM(vb);
	ReleaseCOM(ib);
	ReleaseCOM(normalMap);

	ReleaseCOM(underwaterVB);
	ReleaseCOM(underwaterIB);
	ReleaseCOM(underWaterTex);

	ReleaseCOM(reflectionMapSRV);
	ReleaseCOM(refactionMapSRV);
	ReleaseCOM(reflectionMapRTV);
	ReleaseCOM(refactionMapRTV);
	ReleaseCOM(reflectionMapDSV);
	ReleaseCOM(refactionMapDSV);
}

void TerrainWater::Init(ID3D11Device* device,
	float width, float depth,
	XMFLOAT3 location)
{
	if (!isInit)
	{
		//Get effect
		effect = ShaderManager::GetInstance()->RetrunTerrainWaterFX();
		underwaterEffect = ShaderManager::GetInstance()->ReturnTileForwardTextureMapFX();

		//Init material
		mat.Ambient = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
		mat.Diffuse = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
		mat.Specular = XMFLOAT4(0.6f, 0.6f, 0.6f, 128.0f);
		mat.Reflection = XMFLOAT4(0.2f, 0.28f, 0.32f, 1.0f);

		//load grid
		CreateGrid(device, width, depth);

		//Load udnerwater stuff.
		LoadUnderWaterResources(device, width, depth);

		//Set initial size of render targets (matches screen size)
		reflectionRefactMapSizeW = Application::GetInstance()->ReturnClientWidth();
		reflectionRefactMapSizeH = Application::GetInstance()->ReturnClientHeight();

		//Create refaction/reflection textures
		OnResize(reflectionRefactMapSizeW, reflectionRefactMapSizeH);

		//Set location 
		SetWorldPosition(location);

		//Load Normal Map
		HR(D3DX11CreateShaderResourceViewFromFile(device, 
			L"Terrain/Water/waternormal.dds", 0, 0, &normalMap, 0));

		//Init normal map tex transform data - create in Update(). 
		normTransformScale = XMFLOAT2(width/75.0f, depth/75.0f);
		normTransformScale2 = XMFLOAT2(width/125.0f, depth/125.0f);
		normTransformTrans = XMFLOAT2(0.0f, 0.0f);
		normTransformTrans2 = XMFLOAT2(0.0f, 0.0f);
	
		//Underwater quad world matrix
		XMMATRIX w = XMMatrixTranslation(location.x, location.y-WATER_DEPTH, location.z);
		XMStoreFloat4x4(&underwaterWorld, w);

		//Underwater tex transform
		XMMATRIX uTex = XMMatrixScaling(width/75.0f, depth/75.0f, 1.0f);
		XMStoreFloat4x4(&underwaterTexMat, uTex);

		//Inited so set flag
		isInit = true;

		//Generate AABB
		GenerateAABB();
	}
}

void TerrainWater::OnResize(int w, int h)
{
	//Store new size of render target
	reflectionRefactMapSizeW = w;
	reflectionRefactMapSizeH = h;

	//Recreate the reflection/refaction maps.
	ID3D11Device* device = GraphicsInterface::GetInstance()->GetDevice();
	CreateReflectionMap(device);
	CreateRefactionMap(device);

	//Set viewport for rendering to these texutres.
	reflectRefactViewport.Height = (float)h;
	reflectRefactViewport.Width = (float)w;
	reflectRefactViewport.TopLeftX = 0.0f;
	reflectRefactViewport.TopLeftY = 0.0f;
	reflectRefactViewport.MinDepth = 0.0f;
	reflectRefactViewport.MaxDepth = 1.0f;
}

void TerrainWater::CreateGrid(ID3D11Device* device,
	float width, float depth)
{
	//Create grid
	GeometryGenerator geoGen;
	GeometryGenerator::MeshData data;
	geoGen.CreateGrid(width, depth, 2, 2, data);

	//Create VB
	std::vector<TerrainWaterFX::PerVertex>verts(data.Vertices.size());
	for (UINT i = 0; i < data.Vertices.size(); i++)
	{
		verts[i].PosL = data.Vertices[i].Position;
		verts[i].NormalL = data.Vertices[i].Normal;
		verts[i].TangentL = data.Vertices[i].TangentU;
		verts[i].Tex = data.Vertices[i].TexC;

		//Store position for AABB generation
		sysCopyVerts.push_back(data.Vertices[i].Position);
	}

	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(TerrainWaterFX::PerVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	HR(device->CreateBuffer(&bd, &sd, &vb));

	//Create IB
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();

	sd.pSysMem = &data.Indices[0];

	HR(device->CreateBuffer(&bd, &sd, &ib));

	//Store draw count
	drawCount = data.Indices.size();
}

void TerrainWater::LoadUnderWaterResources(ID3D11Device* device, 
		float width, float depth)
{
	//Create grid
	GeometryGenerator geoGen;
	GeometryGenerator::MeshData data;
	geoGen.CreateGrid(width, depth, 2, 2, data);

	//Create VB
	std::vector<TileForwardTextureMapFX::PosNormalTexVertex>verts(data.Vertices.size());
	for (UINT i = 0; i < data.Vertices.size(); i++)
	{
		verts[i].PosL = data.Vertices[i].Position;
		verts[i].NormalL = data.Vertices[i].Normal;
		verts[i].Tex = data.Vertices[i].TexC;
	}

	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(TileForwardTextureMapFX::PosNormalTexVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	HR(device->CreateBuffer(&bd, &sd, &underwaterVB));

	//Create IB
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();

	sd.pSysMem = &data.Indices[0];

	HR(device->CreateBuffer(&bd, &sd, &underwaterIB));

	//Load tex
	HR(D3DX11CreateShaderResourceViewFromFile(device, 
		L"Terrain/Water/sandfloor009a.jpg", 0, 0, &underWaterTex, 0));

	//Set underwater material. 
	underwaterMat.Ambient = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
	underwaterMat.Diffuse = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
	underwaterMat.Specular = XMFLOAT4(0.0f, 0.0f, 0.0f, 64.0f);
	underwaterMat.Reflection = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
}

void TerrainWater::CreateReflectionMap(ID3D11Device* device)
{
	//Release old data
	ReleaseCOM(reflectionMapSRV);
	ReleaseCOM(reflectionMapRTV);
	ReleaseCOM(reflectionMapDSV);

	//Pointer to our texture - we can release this after creating the views
	//as it wont destroy the resource. (COM)
	ID3D11Texture2D* reflectionTex = 0;
	//Pointer to Texture for the depth buffer
	ID3D11Texture2D* depthTexture = 0;

	//Create texture for reflection map. 
	D3D11_TEXTURE2D_DESC texDesc;
	texDesc.Width = reflectionRefactMapSizeW;
	texDesc.Height = reflectionRefactMapSizeH;
	texDesc.MipLevels = 1;
	texDesc.ArraySize = 1;
	texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	texDesc.SampleDesc.Count = 1;
	texDesc.SampleDesc.Quality = 0;
	texDesc.Usage = D3D11_USAGE_DEFAULT;
	texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	texDesc.CPUAccessFlags = 0;
	texDesc.MiscFlags = 0;

	//Create the texture
	HR(device->CreateTexture2D(&texDesc, 0, &reflectionTex));

	//Create RTV and SRV to this texture
	HR(device->CreateRenderTargetView(reflectionTex, 0, &reflectionMapRTV));
	HR(device->CreateShaderResourceView(reflectionTex, 0, &reflectionMapSRV));

	//Depth buffer for this texture.
	D3D11_TEXTURE2D_DESC depthDesc;
	depthDesc.Width = reflectionRefactMapSizeW;
	depthDesc.Height = reflectionRefactMapSizeH;
	depthDesc.MipLevels = 1;
	depthDesc.ArraySize = 1;
	depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthDesc.SampleDesc.Count = 1;
	depthDesc.SampleDesc.Quality = 0;
	depthDesc.Usage = D3D11_USAGE_DEFAULT;
	depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthDesc.CPUAccessFlags = 0;
	depthDesc.MiscFlags = 0;

	//Create texture
	HR(device->CreateTexture2D(&depthDesc, 0, &depthTexture));

	//DSV to this texture.
	HR(device->CreateDepthStencilView(depthTexture, 0, &reflectionMapDSV));

	//Release the textures
	ReleaseCOM(reflectionTex);
	ReleaseCOM(depthTexture);
}

void TerrainWater::CreateRefactionMap(ID3D11Device* device)
{
	//Release old data.
	ReleaseCOM(refactionMapSRV);
	ReleaseCOM(refactionMapRTV);
	ReleaseCOM(refactionMapDSV);

	//Pointer to our texture - we can release this after creating the views
	//as it wont destroy the resource. (COM)
	ID3D11Texture2D* refactionTex = 0;
	//Pointer to Texture for the depth buffer
	ID3D11Texture2D* depthTexture = 0;

	//Create texture for refaction map. 
	D3D11_TEXTURE2D_DESC texDesc;
	texDesc.Width = reflectionRefactMapSizeW;
	texDesc.Height = reflectionRefactMapSizeH;
	texDesc.MipLevels = 1;
	texDesc.ArraySize = 1;
	texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	texDesc.SampleDesc.Count = 1;
	texDesc.SampleDesc.Quality = 0;
	texDesc.Usage = D3D11_USAGE_DEFAULT;
	texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	texDesc.CPUAccessFlags = 0;
	texDesc.MiscFlags = 0;

	//Create the texture
	HR(device->CreateTexture2D(&texDesc, 0, &refactionTex));

	//Create RTV and SRV to this texture
	HR(device->CreateRenderTargetView(refactionTex, 0, &refactionMapRTV));
	HR(device->CreateShaderResourceView(refactionTex, 0, &refactionMapSRV));

	//Depth buffer for this texture.
	D3D11_TEXTURE2D_DESC depthDesc;
	depthDesc.Width = reflectionRefactMapSizeW;
	depthDesc.Height = reflectionRefactMapSizeH;
	depthDesc.MipLevels = 1;
	depthDesc.ArraySize = 1;
	depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthDesc.SampleDesc.Count = 1;
	depthDesc.SampleDesc.Quality = 0;
	depthDesc.Usage = D3D11_USAGE_DEFAULT;
	depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthDesc.CPUAccessFlags = 0;
	depthDesc.MiscFlags = 0;

	//Create texture
	HR(device->CreateTexture2D(&depthDesc, 0, &depthTexture));

	//DSV to this texture.
	HR(device->CreateDepthStencilView(depthTexture, 0, &refactionMapDSV));

	//Release the textures
	ReleaseCOM(refactionTex);
	ReleaseCOM(depthTexture);
}

void TerrainWater::Update(float delta)
{
	//Translate normal map to give illusion of an animated wave
	normTransformTrans.x += 0.036f * delta * 3;
	normTransformTrans.y += 0.019f * delta * 3;

	normTransformTrans2.x += 0.043f * delta * 3;
	normTransformTrans2.y += 0.025f * delta * 3;

	XMMATRIX s = XMMatrixScaling(normTransformScale.x, normTransformScale.y, 1.0f);
	XMMATRIX t = XMMatrixTranslation(normTransformTrans.x, normTransformTrans.y, 0.0f);

	XMMATRIX s2 = XMMatrixScaling(normTransformScale2.x, normTransformScale2.y, 1.0f);
	XMMATRIX t2 = XMMatrixTranslation(normTransformTrans2.x, normTransformTrans2.y, 0.0f);
	
	//Store
	XMStoreFloat4x4(&normTransform, s*t);
	XMStoreFloat4x4(&normTransform2, s2*t2);
}

void TerrainWater::Render(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		  
		VirtualCamera* playerCamera,										   
		DirecLightShadowMapData* shadowMap,									   
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									  
		ID3D11ShaderResourceView* llb,                                         
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,     
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,    
		DirectionalLight* dLightArr, unsigned dLightCount,					
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						 
		CXMMATRIX &view, CXMMATRIX &proj,									
		ID3D11DeviceContext* context)
{
	//Terrain is an opaque piece of geometry - only render when we should.  
	if ( (isOpaquePass) && (isInit) )
	{
		//Draw water
		DrawWaterLayer(isZPrePass, isOpaquePass, isShadowPass,		  
			playerCamera,										   
			shadowMap,									   
			ssaoMap,													  
			envMap,									  
			llb,                                         
			llibO, llsebO,     
			llibT, llsebT,    
			dLightArr, dLightCount,					
			hemiColDown, hemiColRange,						 
			view, proj,									
			context);

		//Draw underwater layer.
		DrawUnderWaterLayer(isZPrePass, isOpaquePass, isShadowPass,		  
			playerCamera,										   
			shadowMap,									   
			ssaoMap,													  
			envMap,									  
			llb,                                         
			llibO, llsebO,     
			llibT, llsebT,    
			dLightArr, dLightCount,					
			hemiColDown, hemiColRange,						 
			view, proj,									
			context);
	}
}

void TerrainWater::DrawWaterLayer(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		  
		VirtualCamera* playerCamera,										
		DirecLightShadowMapData* shadowMap,								
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									   
		ID3D11ShaderResourceView* llb,                                       
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,  
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   
		CXMMATRIX &view, CXMMATRIX &proj,									
		ID3D11DeviceContext* context)
{
	//Set the correct technique depending. Note: Not a shadow caster
	//just yet. 
	if (isShadowPass)
		return;
	else if (isZPrePass)
		effect->SetActiveTechniqueByName("ZPrePass");
	else
#ifdef USE_LAKE_SHADER
		effect->SetActiveTechniqueByName("Water_Lake");
#else
		effect->SetActiveTechniqueByName("Water");
#endif
	//Render
	context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	context->IASetInputLayout(effect->ReturnInputLayout());

	UINT stride = sizeof(TerrainWaterFX::PerVertex);
	UINT offset = 0;
	context->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
	context->IASetIndexBuffer(ib, DXGI_FORMAT_R32_UINT, 0);

	//Work out global world matrix
	XMMATRIX wd = XMLoadFloat4x4(&worldMatrix);
	XMMATRIX wit = MathHelper::InverseTranspose(wd);
	XMMATRIX wvp = (wd * view) * proj;
	XMMATRIX wv = wd * view;
	//SSAO Transform. 
	XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

	//Set variables.
	effect->SetTextureMap(reflectionMapSRV, refactionMapSRV, normalMap, envMap); 
	effect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, llibO, llsebO); //Opaque
	effect->SetHemisphericalData(hemiColDown, hemiColRange);
	effect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);
	
	if ((!isShadowPass) && (shadowMap))
	{
		effect->SetShadowMap(shadowMap->GetShadowMapSRV());
		effect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
	}
	else
	{
		XMMATRIX i = XMMatrixIdentity();
		effect->SetShadowMap(0);
		effect->SetShadowMapMatricies(i, i, i);
	}

	//Per Object CB
	effect->SetCBufferPerObject(wd,
		wit, 
		wvp, 
		mat,
		wv);
		
	//Tex Teansforms
	effect->SetTexTransforms(XMLoadFloat4x4(&normTransform), 
		XMLoadFloat4x4(&normTransform2));

	//Draw
	for (unsigned p = 0; p < effect->ReturnActiveTechniqueNumberOfPasses(); p++)
	{
		effect->ApplyActiveTechniqueForPass(p, context);
		context->DrawIndexed(drawCount, 0, 0);
	}

	//Unbind resources.
	ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0};
	context->PSSetShaderResources(0, 7, srvNULL);
}

void TerrainWater::DrawUnderWaterLayer(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		  
		VirtualCamera* playerCamera,										
		DirecLightShadowMapData* shadowMap,								
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									   
		ID3D11ShaderResourceView* llb,                                       
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,  
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   
		CXMMATRIX &view, CXMMATRIX &proj,									
		ID3D11DeviceContext* context)
{
	//Set the correct technique depending. Note: Not a shadow caster
	//just yet. 
	if (isShadowPass)
		return;
	else if (isZPrePass)
		underwaterEffect->SetActiveTechniqueByName("ZPrePass_AlphaClip");
	else
		underwaterEffect->SetActiveTechniqueByName("Texture_AlphaClip_NoSSAOSample");

	//Render
	context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	context->IASetInputLayout(underwaterEffect->ReturnInputLayout());

	UINT stride = sizeof(TileForwardTextureMapFX::PosNormalTexVertex);
	UINT offset = 0;
	context->IASetVertexBuffers(0, 1, &underwaterVB, &stride, &offset);
	context->IASetIndexBuffer(underwaterIB, DXGI_FORMAT_R32_UINT, 0);

	//Work out global world matrix
	XMMATRIX wd = XMLoadFloat4x4(&underwaterWorld);
	XMMATRIX wit = MathHelper::InverseTranspose(wd);
	XMMATRIX wvp = (wd * view) * proj;
	XMMATRIX wv = wd * view;
	//SSAO Transform. 
	XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

	//Set variables.
	underwaterEffect->SetTextureMap(underWaterTex); 
	underwaterEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, llibO, llsebO); //Opaque
	underwaterEffect->SetHemisphericalData(hemiColDown, hemiColRange);
	underwaterEffect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);
	underwaterEffect->SetEnvironmentMap(envMap);

	if ((!isShadowPass) && (shadowMap))
	{
		underwaterEffect->SetShadowMap(shadowMap->GetShadowMapSRV());
		underwaterEffect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
	}
	else
	{
		XMMATRIX i = XMMatrixIdentity();
		underwaterEffect->SetShadowMap(0);
		underwaterEffect->SetShadowMapMatricies(i, i, i);
	}

	//Per Object CB
	underwaterEffect->SetCBufferPerObject(wd,
		wit, 
		wvp, 
		XMLoadFloat4x4(&underwaterTexMat),
		underwaterMat,
		wv);

	//Draw
	for (unsigned p = 0; p < underwaterEffect->ReturnActiveTechniqueNumberOfPasses(); p++)
	{
		underwaterEffect->ApplyActiveTechniqueForPass(p, context);
		context->DrawIndexed(drawCount, 0, 0);
	}

	//Unbind resources.
	ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0};
	context->PSSetShaderResources(0, 7, srvNULL);
}

void TerrainWater::RenderReflection(bool isZPrePass, bool isOpaquePass, bool isShadowPass,
		VirtualCamera* playerCamera,										 
		DirecLightShadowMapData* shadowMap,									   
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									    
		ID3D11ShaderResourceView* llb,                                         
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,     
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   
		CXMMATRIX &view, CXMMATRIX &proj,									    
		ID3D11DeviceContext* context, 
		std::vector<TerrainPatchData*>&patches)
{
	//Not a shadow caster - return early.
	if (isShadowPass)
		return;

	//Render surounding patches to the reflection map. 
	//
	//Clip plane we use for reflections
	XMFLOAT4 clipPlane = XMFLOAT4(0.0f, 1.0f, 0.0f, -position.y);

	//We need to generate a new view matrix for use when rendering
	//the reflection
	static VirtualCamera refCam;
	
	//Copy projection data from input player camera to the ref(lect) camera. 
	refCam = *playerCamera;

	//Reflection plane
	XMVECTOR refPlane = XMVectorSet(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w);
	//Camera location
	XMVECTOR camLoc = XMVectorSet(playerCamera->GetPosition().x, 
		playerCamera->GetPosition().y, playerCamera->GetPosition().z, 1.0f);
	
	//Reflect to get new location
	XMVECTOR refLoc = XMVector4Reflect(camLoc, refPlane);

	//Set reflection camera position
	refCam.SetPosition(refLoc);
	 
	//Generate reflected view matrix. 
	XMMATRIX v = playerCamera->GetView();
	XMMATRIX r = XMMatrixReflect(refPlane) * v;

	XMFLOAT4X4 reflectMatrix;
	XMStoreFloat4x4(&reflectMatrix, r);

	//
	//Manually set view of the reflection camera.
	refCam.SetView(reflectMatrix);

	//Previous RTV and DSV - to restore later. 
	ID3D11RenderTargetView* oldRTV = 0;
	ID3D11DepthStencilView* oldDSV = 0;
	//Previous DSS
	ID3D11DepthStencilState* oldDSS = 0;
	UINT oldStencilRef;
	//Previous RS state
	ID3D11RasterizerState* oldRS = 0;

	//Obtain them.
	context->OMGetRenderTargets(1, &oldRTV, &oldDSV);
	context->OMGetDepthStencilState(&oldDSS, &oldStencilRef);
	context->RSGetState(&oldRS);

	//Bind reflection RTV/DSV
	ID3D11RenderTargetView* rtvs[] = {reflectionMapRTV};
	context->OMSetRenderTargets(1, rtvs, reflectionMapDSV);
	
	//Default DSS
	context->OMSetDepthStencilState(NULL, 0);
	//Disable BackFace Culling
	context->RSSetState(RenderStates::noCullRS);

	//Clear reflection tex
	float rtvClear[4] = {0.0f, 0.0f, 0.0f, 1.0f};
	context->ClearRenderTargetView(reflectionMapRTV, rtvClear);
	context->ClearDepthStencilView(reflectionMapDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);

	//Draw skyDome
	GraphicsInterface::GetInstance()->GetCurrentSkyDome()->Render(context, 
		&refCam);

	//Draw surounding patches to the texture
	std::vector<TerrainPatchData*>::iterator it;
	it = patches.begin();
	while(it != patches.end())
	{
		//Render the patch IF it is a surrounding patch. 
		if ((*it)->IsMainTerrainPatch == false)
			RenderPatchReflection(isZPrePass, isOpaquePass, isShadowPass,
				playerCamera,										 
				shadowMap,									   
				ssaoMap,													  
				envMap,									    
				llb,                                         
				llibO, llsebO,     
				llibT, llsebT,     
				dLightArr, dLightCount,					   
				hemiColDown, hemiColRange,						   
				refCam.GetView(), refCam.GetProjection(),									    
				context,
				clipPlane,
				*it);
		
		//Next patch
		it++;
	}

	//Restore old RTV and DSV
	context->OMSetRenderTargets(1, &oldRTV, oldDSV);
	//Restore old DSS
	context->OMSetDepthStencilState(oldDSS, oldStencilRef);
	//Restore old RS
	context->RSSetState(oldRS);

	//Release old pointers (The Get() function increases the reference count, so we
	//need to release to avoid memory leaks)
	ReleaseCOM(oldRTV);
	ReleaseCOM(oldDSV);
	ReleaseCOM(oldDSS);
	ReleaseCOM(oldRS);
}

void TerrainWater::RenderRefaction(bool isZPrePass, bool isOpaquePass, bool isShadowPass,
		VirtualCamera* playerCamera,										 
		DirecLightShadowMapData* shadowMap,									   
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									    
		ID3D11ShaderResourceView* llb,                                         
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,     
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   
		CXMMATRIX &view, CXMMATRIX &proj,									    
		ID3D11DeviceContext* context, 
		std::vector<TerrainPatchData*>&patches)
{
	//Set the correct technique depending. Note: Not a shadow caster
	//just yet. 
	if (isShadowPass)
		return;

	//Calculate clip plane for refaction
	XMFLOAT4 clipPlane = XMFLOAT4(0.0f, -1.0f, 0.0f, position.y + 0.1f);

	//Previous RTV and DSV
	ID3D11RenderTargetView* oldRTV = 0;
	ID3D11DepthStencilView* oldDSV = 0;
	//Previous DSS
	ID3D11DepthStencilState* oldDSS = 0;
	UINT oldStencilRef;

	context->OMGetRenderTargets(1, &oldRTV, &oldDSV);
	context->OMGetDepthStencilState(&oldDSS, &oldStencilRef);

	//Bind refaction RTV/DSV
	ID3D11RenderTargetView* rtvs[] = {refactionMapRTV};
	context->OMSetRenderTargets(1, rtvs, refactionMapDSV);
	
	//Clear refaction tex
	float rtvClear[4] = {0.0f, 0.0f, 0.0f, 1.0f};
	context->ClearRenderTargetView(refactionMapRTV, rtvClear);
	context->ClearDepthStencilView(refactionMapDSV, D3D11_CLEAR_DEPTH, 1.0f, 0);

	//Default DSS
	context->OMSetDepthStencilState(NULL, 0);

	//Draw underwater layer to refaction map.
	if (isZPrePass)
		underwaterEffect->SetActiveTechniqueByName("ZPrePass_AlphaClip_ReflectRefact");
	else
		underwaterEffect->SetActiveTechniqueByName("Texture_AlphaClip_NoSSAOSample_ReflectRefact");

	//Render
	context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	context->IASetInputLayout(underwaterEffect->ReturnInputLayout());

	UINT stride = sizeof(TileForwardTextureMapFX::PosNormalTexVertex);
	UINT offset = 0;
	context->IASetVertexBuffers(0, 1, &underwaterVB, &stride, &offset);
	context->IASetIndexBuffer(underwaterIB, DXGI_FORMAT_R32_UINT, 0);

	//Work out global world matrix
	XMMATRIX wd = XMLoadFloat4x4(&underwaterWorld);
	XMMATRIX wit = MathHelper::InverseTranspose(wd);
	XMMATRIX wvp = (wd * view) * proj;
	XMMATRIX wv = wd * view;
	//SSAO Transform. 
	XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

	//Set variables.
	underwaterEffect->SetTextureMap(underWaterTex); 
	underwaterEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, 0, 0); //Opaque
	underwaterEffect->SetHemisphericalData(hemiColDown, hemiColRange);
	underwaterEffect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);
	underwaterEffect->SetEnvironmentMap(envMap);
	underwaterEffect->SetClipPlane(clipPlane); //Clip anything above water. 

	if ((!isShadowPass) && (shadowMap))
	{
		underwaterEffect->SetShadowMap(shadowMap->GetShadowMapSRV());
		underwaterEffect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
	}
	else
	{
		XMMATRIX i = XMMatrixIdentity();
		underwaterEffect->SetShadowMap(0);
		underwaterEffect->SetShadowMapMatricies(i, i, i);
	}

	//Per Object CB
	underwaterEffect->SetCBufferPerObject(wd,
		wit, 
		wvp, 
		XMLoadFloat4x4(&underwaterTexMat),
		underwaterMat,
		wv);

	//Draw
	for (unsigned p = 0; p < underwaterEffect->ReturnActiveTechniqueNumberOfPasses(); p++)
	{
		underwaterEffect->ApplyActiveTechniqueForPass(p, context);
		context->DrawIndexed(drawCount, 0, 0);
	}

	//Unbind resources.
	ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0};
	context->PSSetShaderResources(0, 7, srvNULL);

	////Draw surounding patches to the texture
	//std::vector<TerrainPatchData*>::iterator it;
	//it = patches.begin();
	//while(it != patches.end())
	//{
	//	//Render the patch IF it is a surrounding patch. 
	//	if ((*it)->IsMainTerrainPatch == false)
	//		RenderPatchReflection(isZPrePass, isOpaquePass, isShadowPass,
	//			playerCamera,										 
	//			shadowMap,									   
	//			ssaoMap,													  
	//			envMap,									    
	//			llb,                                         
	//			llibO, llsebO,     
	//			llibT, llsebT,     
	//			dLightArr, dLightCount,					   
	//			hemiColDown, hemiColRange,						   
	//			playerCamera->GetView(), playerCamera->GetProjection(),									    
	//			context,
	//			clipPlane,
	//			*it);
	//	
	//	//Next patch
	//	it++;
	//}

	//Restore old RTV and DSV
	context->OMSetRenderTargets(1, &oldRTV, oldDSV);
	//Restore old DSS
	context->OMSetDepthStencilState(oldDSS, oldStencilRef);

	//Release old pointers (The Get() function increases the reference count, so we
	//need to release to avoid memory leaks)
	ReleaseCOM(oldRTV);
	ReleaseCOM(oldDSV);
	ReleaseCOM(oldDSS);
}

void TerrainWater::RenderPatchReflection(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		  
		VirtualCamera* playerCamera,										
		DirecLightShadowMapData* shadowMap,								
		SSAOData* ssaoMap,													  
		ID3D11ShaderResourceView* envMap,									   
		ID3D11ShaderResourceView* llb,                                       
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,  
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					   
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   
		CXMMATRIX &view, CXMMATRIX &proj,									
		ID3D11DeviceContext* context, 
		XMFLOAT4 &clipPlane,
		TerrainPatchData* patch)
{
	//Get the terrain effect
	static TerrainFX* terrainEffect = NULL;
	terrainEffect = ShaderManager::GetInstance()->ReturnTerrainFX();

	//Terrain is an opaque piece of geometry - only render when we should.  
	if ( (isOpaquePass) && (isInit) )
	{
		//Set the correct technique depending. Note: Not a shadow caster
		//just yet. 
		if (patch->Patch->useSlopeBasedRendering)
			terrainEffect->SetActiveTechniqueByName("Terrain_ReflectRefact");
		else
			terrainEffect->SetActiveTechniqueByName("Terrain_Height_ReflectRefact");

		//Render
		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		context->IASetInputLayout(terrainEffect->ReturnInputLayout());

		UINT stride = sizeof(TerrainFX::PerVertex);
		UINT offset = 0;
		context->IASetVertexBuffers(0, 1, &patch->Patch->terrainLayerVB, &stride, &offset);
		context->IASetIndexBuffer(patch->Patch->terrainLayerIB, DXGI_FORMAT_R32_UINT, 0);

		//Work out global world matrix
		XMMATRIX wd = XMLoadFloat4x4(&patch->Patch->worldMatrix);
		XMMATRIX wit = MathHelper::InverseTranspose(wd);
		XMMATRIX wvp = (wd * view) * proj;
		XMMATRIX wv = wd * view;
		//SSAO Transform. 
		XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

		//Set variables.
		terrainEffect->SetTextureMap(patch->Patch->terrainTexData->DiffuseMap0, 
			patch->Patch->terrainTexData->DetailMap0, 
			patch->Patch->terrainTexData->NormalMap0, 
			patch->Patch->terrainTexData->DiffuseMap1, 
			patch->Patch->terrainTexData->NormalMap1, 
			patch->Patch->terrainTexData->DiffuseMap2, 
			patch->Patch->terrainTexData->NormalMap2); 
		terrainEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, 0, 0);
		terrainEffect->SetHemisphericalData(hemiColDown, hemiColRange);
		terrainEffect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);

		if ((!isShadowPass) && (shadowMap))
		{
			terrainEffect->SetShadowMap(shadowMap->GetShadowMapSRV());
			terrainEffect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
		}
		else
		{
			XMMATRIX i = XMMatrixIdentity();
			terrainEffect->SetShadowMap(0);
			terrainEffect->SetShadowMapMatricies(i, i, i);
		}

		//Per Object CB
		terrainEffect->SetCBufferPerObject(wd,
			wit, 
			wvp, 
			patch->Patch->terrainMaterial,
			wv);
		
		//Tex Teansforms
		terrainEffect->SetTexTransforms(XMLoadFloat4x4(&patch->Patch->terrainTexTrans), 
			XMLoadFloat4x4(&patch->Patch->terrainTexDetailTrans));

		//if this is not slope based (height based), then set the height data
		if (!patch->Patch->useSlopeBasedRendering)
			terrainEffect->SetHeightBasedPatchValues(patch->Patch->heightFac1, patch->Patch->heightFac2);

		//Draw
		for (unsigned p = 0; p < terrainEffect->ReturnActiveTechniqueNumberOfPasses(); p++)
		{
			terrainEffect->ApplyActiveTechniqueForPass(p, context);
			context->DrawIndexed(patch->Patch->terrainLayerDrawCount, 0, 0);
		}

		//Unbind resources.
		ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0};
		context->PSSetShaderResources(0, 12, srvNULL);
	}

	//Reset RS state (incase its changed to wireframe)
	//context->RSSetState(NULL);
}

void TerrainWater::DebugDrawRefactionTexture(ID3D11DeviceContext* context)
{
	D3D11Renderer::GetInstance()->DebugDrawTexture(context, 
		GraphicsInterface::GetInstance()->ReturnDebugDrawQuadVB(), 
		GraphicsInterface::GetInstance()->ReturnDebugDrawQuadIB(), 
		refactionMapSRV, XMMatrixIdentity(), false, "RGBA");
}

void TerrainWater::DebugDrawReflectionTexture(ID3D11DeviceContext* context)
{
	D3D11Renderer::GetInstance()->DebugDrawTexture(context, 
		GraphicsInterface::GetInstance()->ReturnDebugDrawQuadVB(), 
		GraphicsInterface::GetInstance()->ReturnDebugDrawQuadIB(), 
		reflectionMapSRV, XMMatrixIdentity(), false, "RGBA");
}